package cn.xeblog.plugin.game.landlords;

import cn.hutool.core.collection.CollectionUtil;
import cn.xeblog.commons.entity.game.landlords.Poker;
import cn.xeblog.commons.entity.game.landlords.PokerInfo;
import cn.xeblog.commons.entity.game.landlords.PokerModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;

/**
 * AI玩家行为
 *
 * @author anlingyi
 * @date 2022/6/3 3:19 下午
 */
public class AIPlayerAction extends PlayerAction {

  /**
   * 扑克牌缓存[牌值,扑克牌列表]
   */
  private Map<Integer, List<Poker>> pokersMap;
  /**
   * 单张列表
   */
  private List<Integer> singleList;
  /**
   * 对子列表
   */
  private List<Integer> pairList;
  /**
   * 三张列表
   */
  private List<Integer> threeList;
  /**
   * 炸弹列表
   */
  private List<Integer> bombList;
  /**
   * 王列表
   */
  private List<Integer> kingList;

  /**
   * 构造
   *
   * @param playerNode 玩家节点
   */
  public AIPlayerAction(PlayerNode playerNode) {
    super(playerNode);
  }

  @Override
  public int callScore(int score) {
    // 构建扑克模型
    buildPokerModel(pokers);

    int call = 0;
    // 当有1个王或者有至少1个炸弹，叫3分
    if (kingList.size() > 1 || bombList.size() > 0) {
      call = 3;
    } else if (kingList.size() == 1 && getPlain() != null) {
      // 当有1个王 且有至少一个飞机，叫2分
      call = 2;
    } else if (kingList.size() == 1 && (getShunzi(true) != null || getShunzi(false) != null || getMax(threeList) > 13)) {
      // 当有1个王且（有顺子，或者最大的三张比k大），叫1分
      call = 1;
    }

    // 叫的分小于等于上一玩家叫分，相当于叫0分
    if (call <= score) {
      call = 0;
    }

    return call;
  }

  @Override
  public PokerInfo processOutPoker(PlayerNode outPlayer, PokerInfo pokerInfo) {
    // 构建手牌模型
    buildPokerModel(pokers);
    // 是否出炸弹（机器人是根据情况考虑是否出炸弹的）
    boolean canBomb = false;
    // 出牌信息
    PokerInfo out = null;

    // 存在牌型信息
    if (pokerInfo != null) {
      // 手牌数量少于指定牌型，即手牌不够，直接返回
      if (pokers.size() < pokerInfo.getPokers().size()) {
        return null;
      }

      int outValue;
      // 牌型匹配
      switch (pokerInfo.getPokerModel()) {
        case SINGLE:
          // 单张列表
          List<Integer> copySingleList = new ArrayList<>(singleList);
          // 单王加入单张列表
          if (kingList.size() == 1) {
            copySingleList.add(kingList.get(0));
          }
          // 找到在单张列表中第一个大于出牌值
          outValue = getBiggerThanIt(copySingleList, pokerInfo.getValue());
          // 存在值，设置出牌信息
          if (outValue > 0) {
            out = PokerUtil.getPokerInfo(pokersMap.get(outValue));
          }
          break;
        case PAIR:
          // 找到在对子列表中第一个大于出牌值
          outValue = getBiggerThanIt(pairList, pokerInfo.getValue());
          // 存在值，设置出牌信息
          if (outValue > 0) {
            out = PokerUtil.getPokerInfo(pokersMap.get(outValue));
          }
          break;
        case THREE:
          // 找到在三张列表中第一个大于出牌值
          outValue = getBiggerThanIt(threeList, pokerInfo.getValue());
          // 存在值，设置出牌信息
          if (outValue > 0) {
            out = PokerUtil.getPokerInfo(pokersMap.get(outValue));
          }
          break;
        case THREE_ONE_SINGLE:
          // 找到在三张列表中第一个大于出牌值
          outValue = getBiggerThanIt(threeList, pokerInfo.getValue());
          // 存在值时
          if (outValue > 0) {
            // 找到三张牌型的牌
            List<Poker> outPokers = pokersMap.get(outValue);
            // 最小的单张
            int minPoker = getMin(singleList);
            // 存在最小的单张时，加入出牌的列表中
            if (minPoker > 0) {
              outPokers.addAll(pokersMap.get(minPoker));
            } else {
              // 不存在最小的单张时，从对子中找
              minPoker = getMin(pairList);
              // 如果没有从对子中找到,则从三张中找
              if (minPoker == 0) {
                minPoker = getMin(threeList);
              }
              // 存在时，加入出牌的列表中
              if (minPoker > 0) {
                outPokers.add(pokersMap.get(minPoker).get(0));
              }
            }
            // 存在值，设置出牌信息
            if (outPokers.size() > 0) {
              out = PokerUtil.getPokerInfo(outPokers);
            }
          }
          break;
        case THREE_ONE_PAIR:
          // 找到在三张列表中第一个大于出牌值
          outValue = getBiggerThanIt(threeList, pokerInfo.getValue());
          // 存在值时
          if (outValue > 0) {
            // 三张的牌型列表
            List<Poker> outPokers = pokersMap.get(outValue);
            // 最小的对子
            int minPoker = getMin(pairList);

            if (minPoker > 0) {
              outPokers.addAll(pokersMap.get(minPoker));
            } else {
              // 不存在，找最小的三张
              minPoker = getMin(threeList);
              // 存在，加入出牌列表
              if (minPoker > 0) {
                outPokers.addAll(pokersMap.get(minPoker).subList(0, 2));
              }
            }
            // 存在值，设置出牌信息
            if (outPokers.size() > 0) {
              out = PokerUtil.getPokerInfo(outPokers);
            }
          }
          break;
        case PLAIN_MANNED:
        case PLAIN_UNMANNED:
          // 标记为出炸弹
          canBomb = true;
          // 获取飞机
          out = getPlain(pokerInfo);
          break;
        case SHUN_ZI_PAIR:
        case SHUN_ZI_SINGLE:
          // 标记为出炸弹
          canBomb = true;
          // 获取顺子
          out = getShunzi(pokerInfo);
          break;
        case BOMB:
          // 找到在对子列表中第一个大于出牌值
          outValue = getBiggerThanIt(bombList, pokerInfo.getValue());
          // 存在值，设置出牌信息
          if (outValue > 0) {
            out = PokerUtil.getPokerInfo(pokersMap.get(outValue));
          }
          break;
        default:
          // 标记为出炸弹
          canBomb = true;
          break;
      }

      // 农民且手牌数小于6张，标记为“出炸弹”（机器人是根据情况考虑是否出炸弹的）
      if (playerNode.getRole() == 1) {
        if (playerNode.getPokerTotal() < 6) {
          canBomb = true;
        }
      } else if (playerNode.getPokerTotal() < 10 || playerNode.getNextPlayer().getPokerTotal() < 10
          || playerNode.getPrevPlayer().getPokerTotal() < 10) {
        // 如果玩家手牌小于10 或者下一个玩家手牌小于10 或者上一个玩家手牌小于10，那么标记为“出炸弹”（机器人是根据情况考虑是否出炸弹的）
        canBomb = true;
      }

      // 出牌信息为空，且能出炸弹
      if (out == null && canBomb) {
        // 如果炸弹数不为空，出一个最小的炸弹
        if (bombList.size() > 0) {
          out = PokerUtil.getPokerInfo(pokersMap.get(getMin(bombList)));
        }
        // 出牌信息为空且有双王，出双王
        if (out == null && kingList.size() > 1) {
          List<Poker> kingPokerList = new ArrayList<>();
          kingList.forEach(val -> kingPokerList.add(pokersMap.get(val).get(0)));
          out = PokerUtil.getPokerInfo(kingPokerList);
        }
      }
    } else { // 不存在牌型信息

      // 获取飞机
      out = getPlain();
      // 没有飞机则获取顺对子
      if (out == null) {
        out = getShunzi(false);
      }
      // 没有则获取单张顺子
      if (out == null) {
        out = getShunzi(true);
      }
      // 没有则获取三张
      if (out == null) {
        out = getThree();
      }
      // 没有则获取对子
      if (out == null) {
        out = getPair();
      }
      // 没有获取到
      if (out == null) {
        // 是否获取最大
        boolean maxed = false;
        // 下一玩家角色与当前角色不一致时
        if (playerNode.getNextPlayer().getRole() != playerNode.getRole()) {
          // 当手牌数量等于2且单牌数量为2或者下一玩家手牌总量小于3
          maxed = pokers.size() == 2 && singleList.size() == 2 || playerNode.getNextPlayer().getPokerTotal() < 3;
        }
        // 按条件获取单张
        out = getSingle(maxed);
      }
      // 没有获取到，出全部手牌
      if (out == null) {
        out = PokerUtil.getPokerInfo(pokers);
      }
      // 没有获取到，出王
      if (out == null && kingList.size() == 1) {
        out = PokerUtil.getPokerInfo(pokersMap.get(kingList.get(0)));
      }
      // 没有获取到，出炸弹
      if (out == null && bombList.size() > 0) {
        out = PokerUtil.getPokerInfo(pokersMap.get(getMin(bombList)));
      }
      // 没有获取到，出双王
      if (out == null) {
        if (kingList.size() == 2) {
          List<Poker> kingPoker = new ArrayList<>();
          kingPoker.add(pokersMap.get(16).get(0));
          kingPoker.add(pokersMap.get(17).get(0));
          out = PokerUtil.getPokerInfo(kingPoker);
        }
      }
    }

    // 如果可以出牌且出牌不大于上一家出的，返回null
    if (out != null && !out.biggerThanIt(pokerInfo)) {
      return null;
    }

    return out;
  }

  /**
   * 构建扑克模式列表
   */
  protected void buildPokerModel() {
    buildPokerModel(this.pokers);
  }

  /**
   * 构建扑克模型
   *
   * @param pokers 扑克牌列表
   */
  protected void buildPokerModel(List<Poker> pokers) {
    // 单张列表
    singleList = new ArrayList<>();
    // 对子列表
    pairList = new ArrayList<>();
    // 三张列表
    threeList = new ArrayList<>();
    // 炸弹列表
    bombList = new ArrayList<>();
    // 王列表
    kingList = new ArrayList<>();

    // 通过牌值生成扑克Map
    List<Poker> pokerList = new ArrayList<>(pokers);
    pokersMap = pokerList.stream().collect(Collectors.groupingBy(Poker::getValue));

    pokersMap.forEach((k, v) -> {
      // 王
      if (k > 15) {
        kingList.add(k);
        return;
      }

      int size = v.size();

      // 单张
      if (size == 1) {
        singleList.add(k);
      } else if (size == 2) {
        // 对子
        pairList.add(k);
      } else if (size == 3) {
        // 三张
        threeList.add(k);
      } else {
        // 炸弹
        bombList.add(k);
      }
    });

    // 排序
    Collections.sort(singleList);
    Collections.sort(pairList);
    Collections.sort(threeList);
    Collections.sort(bombList);
  }

  /**
   * 找到在列表中第一个大于指定值的数
   *
   * @param list  值列表
   * @param value 指定值
   * @return 在列表中第一个大于指定值的数
   */
  private int getBiggerThanIt(List<Integer> list, int value) {
    return list.stream().filter(val -> val > value).findFirst().orElse(0);
  }

  /**
   * 获取最小的值
   *
   * @param list 已排序的值列表（从小到大）
   * @return 最小的值
   */
  private int getMin(List<Integer> list) {
    return CollectionUtil.isEmpty(list) ? 0 : list.get(0);
  }

  /**
   * 获取最大的值
   *
   * @param list 已排序的值列表（从小到大）
   * @return 最大的值
   */
  private int getMax(List<Integer> list) {
    return CollectionUtils.isEmpty(list) ? 0 : list.get(list.size() - 1);

  }

  /**
   * 获取飞机（无人飞机、载人飞机），即三张一样的牌为一组，至少需要两组以上，并且每组牌必须是连着的，且不带牌(当数量不足时，补足所需带牌的数量，单片不够拆对子，对子不够拆三张)
   *
   * @param pokerInfo 扑克组相关信息
   * @return 扑克组相关信息
   */
  private PokerInfo getPlain(PokerInfo pokerInfo) {

    // 最小值
    int minValue = pokerInfo.getPokers().get(0).getValue();
    // 最大值
    int maxValue = pokerInfo.getValue();
    // 顺子长度
    int shunziLen = maxValue - minValue + 1;
    // 三张列表的大小
    int size = threeList.size();
    // 最大值与最小值的差值小于三张的个数，说明不连续
    if (size < shunziLen) {
      return null;
    }
    // 顺子默认是1
    int shunzi = 1;
    // 顺子扑克值列表
    List<Integer> shunziPokerValues = new ArrayList<>();
    // 获取三张的列表
    for (int i = 0; i < size; i++) {
      // 如果为最后一个，跳过
      if (i + 1 == size) {
        break;
      }

      // 当前值
      int currValue = threeList.get(i);
      // 下一个值
      int nexValue = threeList.get(i + 1);
      // 当前值小于最小值或者当前值大于A或者下一位值大于A,都无法构成连续的，跳过
      if (currValue < minValue || currValue > 14 || nexValue > 14) {
        continue;
      }

      // 当前值与下一个值差值为1，说明是连续的,加入顺子列表，
      if (currValue + 1 == nexValue) {
        if (shunzi == 1) {
          shunziPokerValues.add(currValue);
        }
        shunziPokerValues.add(nexValue);
        shunzi++;
      } else {
        // 非连续的则重置
        shunzi = 1;
        shunziPokerValues.clear();
      }

      // 顺子与顺子的长度相等，跳出循环
      if (shunzi == shunziLen) {
        break;
      }
    }

    // 长度不一样，说明不是飞机
    if (shunziPokerValues.size() != shunziLen) {
      return null;
    }

    List<Poker> pokerList = new ArrayList<>();
    // 将飞机加入扑克牌列表
    shunziPokerValues.forEach(val -> pokerList.addAll(pokersMap.get(val)));

    // 如果牌型是载人飞机
    if (pokerInfo.getPokerModel() == PokerModel.PLAIN_MANNED) {
      // 总出牌数
      int outTotal = pokerInfo.getPokers().size();
      // 带牌的数量
      int withTotal = outTotal - shunziLen * 3;
      // 如果带牌的数量等于顺子的2倍,说明带的是对子
      if (withTotal == shunziLen * 2) {
        // 带对子的数量
        int pairTotal = withTotal / 2;
        // 对子列表数量小于当前带对子的数量，数量不匹配，返回null，
        if (pairList.size() < pairTotal) {
          return null;
        }
        // 将对子加入扑克牌列表
        pairList.subList(0, pairTotal).forEach(val -> pokerList.addAll(pokersMap.get(val)));
      } else {
        // 单张的数量
        int singleSize = singleList.size();
        // 剩余的数量
        int surplus = withTotal;
        // 存在单张
        if (singleSize > 0) {
          // 带单张的数量,有可能牌不够，所以找单张与允许带单张最大量的最小值
          int withSingleSize = Math.min(singleSize, withTotal);
          // 剩余的数量
          surplus -= withSingleSize;
          // 将单张加入扑克牌列表
          singleList.subList(0, withSingleSize).forEach(val -> pokerList.addAll(pokersMap.get(val)));
        }

        // 如果还有剩余的量
        if (surplus > 0) {
          // 对子列表不为空
          if (pairList.size() > 0) {
            out:
            // 遍历对子值列表
            for (Integer val : pairList) {
              // 遍历对子
              for (Poker poker : pokersMap.get(val)) {
                // 将对子加入扑克牌列表
                pokerList.add(poker);
                // 自减至0后跳出循环
                if (--surplus == 0) {
                  break out;
                }
              }
            }
          }
          // 如果还有剩余
          if (surplus > 0) {
            // 三张列表
            List<Integer> copyThreeList = new ArrayList<>(threeList);
            // 移除当前的飞机
            copyThreeList.removeAll(shunziPokerValues);
            // 仍存在三张
            if (copyThreeList.size() > 0) {
              out:
              // 遍历三张值列表
              for (Integer val : copyThreeList) {
                // 遍历三张
                for (Poker poker : pokersMap.get(val)) {
                  pokerList.add(poker);
                  if (--surplus == 0) {
                    // 自减至0后跳出循环
                    break out;
                  }
                }
              }
            }
          }
        }
      }
    }
    // 出牌信息
    return PokerUtil.getPokerInfo(pokerList);
  }

  /**
   * 获取顺子(单顺或者对子顺)
   *
   * @param pokerInfo 扑克组相关信息
   * @return 扑克组相关信息
   */
  private PokerInfo getShunzi(PokerInfo pokerInfo) {
    // 是否为单顺
    boolean isSingle = pokerInfo.getPokerModel() == PokerModel.SHUN_ZI_SINGLE;

    int minValue = pokerInfo.getPokers().get(0).getValue();
    int maxValue = pokerInfo.getValue();
    // 顺子的长度，即最小与最大的差值+1
    int shunziLen = maxValue - minValue + 1;
    // 扑克值列表
    List<Integer> pokerValue = new ArrayList<>();
    // 是单张，单张列表加入扑克值列表
    if (isSingle) {
      pokerValue.addAll(singleList);
    }
    // 对子、三张加入扑克值列表
    pokerValue.addAll(pairList);
    pokerValue.addAll(threeList);
    // 牌值排序
    Collections.sort(pokerValue);

    // 值列表数量
    int size = pokerValue.size();
    // 扑克值列表 小于顺子长度，说明范围不足，返回空，
    if (size < shunziLen) {
      return null;
    }

    // 顺子的长度
    int shunzi = 1;
    // 顺子扑克值列表
    List<Integer> shunziPokerValues = new ArrayList<>();
    for (int i = 0; i < size; i++) {
      // 最后一个不处理理
      if (i + 1 == size) {
        break;
      }

      // 当前值
      int currValue = pokerValue.get(i);
      // 下一位的值
      int nexValue = pokerValue.get(i + 1);
      // 当前值小于最小修士或者当前值大于A或者最大值大于A,都无法构成连续的，跳过
      if (currValue < minValue || currValue > 14 || nexValue > 14) {
        continue;
      }

      // 当前值与下一个值差值为1，说明是连续的,加入顺子列表，
      if (currValue + 1 == nexValue) {
        if (shunzi == 1) {
          shunziPokerValues.add(currValue);
        }
        shunziPokerValues.add(nexValue);
        shunzi++;
      } else {
        // 非连续的则重置
        shunzi = 1;
        shunziPokerValues.clear();
      }

      // 顺子与顺子的长度相等，跳出循环
      if (shunzi == shunziLen) {
        break;
      }
    }

    // 长度不一样，说明不是指定的单顺
    if (shunziPokerValues.size() != shunziLen) {
      return null;
    }

    // 将单张或者对子加入扑克牌列表
    List<Poker> pokerList = new ArrayList<>();
    shunziPokerValues.forEach(val -> pokerList.addAll(pokersMap.get(val).subList(0, isSingle ? 1 : 2)));
    // 出牌信息
    return PokerUtil.getPokerInfo(pokerList);
  }

  /**
   * 获取飞机
   *
   * @return 扑克组相关信息
   */
  public PokerInfo getPlain() {
    // 三张的数量
    int size = threeList.size();
    // 飞机至少需要两组三张及以上
    if (size < 2) {
      return null;
    }

    // 顺子最大的扑克值列表
    List<Integer> maxShunziPokerValues = null;
    // 顺子最大的长度
    int maxShunzi = 0;
    // 顺子长度
    int shunzi = 1;
    // 顺子扑克值列表
    List<Integer> shunziPokerValues = new ArrayList<>();

    // 遍历当前“三张列表”
    for (int i = 0; i < size; i++) {
      // 顺子大于最大顺子，则更新最大顺子相关信息
      if (shunzi > maxShunzi) {
        maxShunzi = shunzi;
        maxShunziPokerValues = shunziPokerValues;
      }
      // 如果顺子等于1
      if (shunzi == 1) {
        // 顺子大于1，跳出，说明有飞机了
        if (maxShunzi > 1) {
          break;
        }
        // 清空顺子扑克值列表
        shunziPokerValues.clear();
      }

      // 最后一位不操作，跳过
      if (i + 1 == size) {
        break;
      }

      // 当前值
      int currValue = threeList.get(i);
      // 下一个值
      int nexValue = threeList.get(i + 1);
      // 当前值大于A或者下一位值大于A,都无法构成连续的，跳过
      if (currValue > 14 || nexValue > 14) {
        continue;
      }
      // 当前值与下一个值差值为1，说明是连续的,加入顺子列表，
      if (currValue + 1 == nexValue) {
        if (shunzi == 1) {
          shunziPokerValues.add(currValue);
        }
        shunziPokerValues.add(nexValue);
        shunzi++;
      } else {
        // 非连续的则重置
        shunzi = 1;
      }
    }

    // 没有飞机，返回空
    if (maxShunzi < 2) {
      return null;
    }
    // 将最大飞机加入扑克牌列表
    List<Poker> pokerList = new ArrayList<>();
    maxShunziPokerValues.forEach(val -> pokerList.addAll(pokersMap.get(val)));

    // 能带的总数量
    int withTotal = maxShunzi;
    // 对子的数据大于或等于能带的总数量
    if (pairList.size() >= withTotal) {
      // 将对子加入扑克牌列表
      pairList.subList(0, withTotal).forEach(val -> pokerList.addAll(pokersMap.get(val)));
    } else {
      // 单张的数量
      int singleSize = singleList.size();
      // 剩余的数量
      int surplus = withTotal;
      // 存在单张
      if (singleSize > 0) {
        // 带单张的数量,有可能牌不够，所以找单张与允许带单张最大量的最小值
        int withSingleSize = Math.min(singleSize, withTotal);
        // 剩余的数量
        surplus -= withSingleSize;
        // 将单张加入扑克牌列表
        singleList.subList(0, withSingleSize).forEach(val -> pokerList.addAll(pokersMap.get(val)));
      }

      // 如果还有剩余的量
      if (surplus > 0) {
        // 对子列表不为空
        if (pairList.size() > 0) {
          out:
          // 遍历对子值列表
          for (Integer val : pairList) {
            // 遍历对子
            for (Poker poker : pokersMap.get(val)) {
              // 将对子加入扑克牌列表
              pokerList.add(poker);
              // 自减至0后跳出循环
              if (--surplus == 0) {
                break out;
              }
            }
          }
        }
        // 如果还有剩余
        if (surplus > 0) {
          // 三张列表
          List<Integer> copyThreeList = new ArrayList<>(threeList);
          // 移除当前的飞机
          copyThreeList.removeAll(shunziPokerValues);
          // 仍存在三张
          if (copyThreeList.size() > 0) {
            out:
            // 遍历三张值列表
            for (Integer val : copyThreeList) {
              // 遍历三张
              for (Poker poker : pokersMap.get(val)) {
                pokerList.add(poker);
                if (--surplus == 0) {
                  // 自减至0后跳出循环
                  break out;
                }
              }
            }
          }
        }
      }
    }
    // 出牌信息
    return PokerUtil.getPokerInfo(pokerList);
  }

  /**
   * 获取顺子
   *
   * @param isSingle 是否单张
   * @return 扑克组相关信息
   */
  public PokerInfo getShunzi(boolean isSingle) {
    List<Integer> pokerValue = new ArrayList<>();
    // 是单张，单张列表加入扑克值列表
    if (isSingle) {
      pokerValue.addAll(singleList);
    }
    // 对子、三张加入扑克值列表
    pokerValue.addAll(pairList);
    pokerValue.addAll(threeList);
    // 牌值排序
    Collections.sort(pokerValue);

    // 值列表数量
    int size = pokerValue.size();
    // 顺子的长度
    int shunzi = 1;
    // 顺子最大的长度
    int maxShunzi = 0;
    // 顺子最大的扑克值列表
    List<Integer> maxShunziPokerValues = null;
    // 顺子扑克值列表
    List<Integer> shunziPokerValues = new ArrayList<>();

    // 遍历当前扑克值列表
    for (int i = 0; i < size; i++) {
      // 顺子大于最大顺子，则更新最大顺子相关信息
      if (shunzi > maxShunzi) {
        maxShunzi = shunzi;
        maxShunziPokerValues = shunziPokerValues;
      }
      // 如果顺子等于1
      if (shunzi == 1) {
        // 顺子数量大于4，或者 非单张且顺对子数量大于3，跳出，说明有顺子
        if (maxShunzi > 4 || (!isSingle && maxShunzi > 2)) {
          break;
        }
        // 清空顺子扑克值列表
        shunziPokerValues.clear();
      }

      // 最后一位不操作，跳过
      if (i + 1 == size) {
        break;
      }

      // 当前值
      int currValue = pokerValue.get(i);
      // 下一个值
      int nexValue = pokerValue.get(i + 1);
      // 当前值大于A或者下一位值大于A,都无法构成连续的，跳过
      if (currValue > 14 || nexValue > 14) {
        continue;
      }

      // 当前值与下一个值差值为1，说明是连续的,加入顺子列表
      if (currValue + 1 == nexValue) {
        if (shunzi == 1) {
          shunziPokerValues.add(currValue);
        }
        shunziPokerValues.add(nexValue);
        shunzi++;
      } else {
        // 非连续的则重置
        shunzi = 1;
      }
    }

    // 单张且没有顺子，返回空
    if (isSingle) {
      if (maxShunzi < 5) {
        return null;
      }
    } else if (maxShunzi < 3) {
      // 对子，且没有顺子，返回空
      return null;
    }

    // 将最大顺子加入扑克牌列表
    List<Poker> pokerList = new ArrayList<>();
    maxShunziPokerValues.forEach(val -> pokerList.addAll(pokersMap.get(val).subList(0, isSingle ? 1 : 2)));
    // 出牌信息
    return PokerUtil.getPokerInfo(pokerList);
  }

  /**
   * 获取三张
   *
   * @return 扑克组相关信息，获取失败，返回null
   */
  public PokerInfo getThree() {
    // 获取最小的三张
    int value = getMin(threeList);
    // 未获取到，返回null
    if (value == 0) {
      return null;
    }

    // 获取三张的列表
    List<Poker> pokerList = pokersMap.get(value);
    //如果对子不为空，将最小的对子加入出牌列表
    if (pairList.size() > 0) {
      pokerList.addAll(pokersMap.get(getMin(pairList)));
    } else if (singleList.size() > 0) {
      //如果单牌不为空，将最小的单牌加入出牌列表
      pokerList.add(pokersMap.get(getMin(singleList)).get(0));
    }
    // 出牌信息
    return PokerUtil.getPokerInfo(pokerList);
  }

  /**
   * 获取对子
   *
   * @return 扑克组相关信息，获取失败，返回null
   */
  public PokerInfo getPair() {
    // 获取最小的对子
    int value = getMin(pairList);
    // 未获取到，返回null
    if (value == 0) {
      return null;
    }
    // 出牌信息
    return PokerUtil.getPokerInfo(pokersMap.get(value));
  }

  /**
   * 获取单张
   *
   * @return 扑克组相关信息，获取失败，返回null
   */
  public PokerInfo getSingle() {
    return getSingle(false);
  }

  /**
   * 获取单张
   *
   * @param maxed 是否获取最大
   * @return 扑克组相关信息，获取失败，返回null
   */
  public PokerInfo getSingle(boolean maxed) {
    // 单牌值
    int value = maxed ? getMax(singleList) : getMin(singleList);
    // 未获取到，返回null
    if (value == 0) {
      return null;
    }
    // 出牌信息
    return PokerUtil.getPokerInfo(pokersMap.get(value));
  }

  public static void main(String[] args) {
//        test1();
    test2();
  }

  private static void test1() {
    // 测试AI出小牌放队友走

    PlayerNode playerNode = new PlayerNode();
    playerNode.setPlayer("1");
    playerNode.setRole(1);
    PlayerNode playerNode2 = new PlayerNode();
    playerNode2.setPlayer("2");
    playerNode2.setRole(1);
    // 队友只剩一张牌了
    playerNode2.setPokerTotal(1);
    PlayerNode playerNode3 = new PlayerNode();
    playerNode3.setPlayer("3");
    playerNode3.setRole(2);

    playerNode.setPrevPlayer(playerNode3);
    playerNode.setNextPlayer(playerNode2);
    playerNode2.setPrevPlayer(playerNode);
    playerNode2.setNextPlayer(playerNode3);
    playerNode3.setPrevPlayer(playerNode2);
    playerNode3.setNextPlayer(playerNode);

    AIPlayerAction playerAction = new AIPlayerAction(playerNode);

    List<Poker> pokers = new ArrayList<>();
    pokers.add(new Poker(3, Poker.Suits.SPADE));
    pokers.add(new Poker(6, Poker.Suits.CLUB));
    pokers.add(new Poker(10, Poker.Suits.CLUB));
    playerAction.setPokers(pokers);
    playerAction.buildPokerModel();

    // AI应该出最小的3放队友走
    System.out.println(playerAction.outPoker(null, null));
    ;
  }

  private static void test2() {
    // 测试AI出大牌压下家地主

    PlayerNode playerNode = new PlayerNode();
    playerNode.setPlayer("1");
    playerNode.setRole(1);
    PlayerNode playerNode2 = new PlayerNode();
    playerNode2.setPlayer("2");
    playerNode2.setRole(2);
    // 地主只剩1张牌了
    playerNode2.setPokerTotal(1);
    PlayerNode playerNode3 = new PlayerNode();
    playerNode3.setPlayer("3");
    playerNode3.setRole(1);

    playerNode.setPrevPlayer(playerNode3);
    playerNode.setNextPlayer(playerNode2);
    playerNode2.setPrevPlayer(playerNode);
    playerNode2.setNextPlayer(playerNode3);
    playerNode3.setPrevPlayer(playerNode2);
    playerNode3.setNextPlayer(playerNode);

    AIPlayerAction playerAction = new AIPlayerAction(playerNode);

    List<Poker> pokers = new ArrayList<>();
    pokers.add(new Poker(3, Poker.Suits.SPADE));
    pokers.add(new Poker(6, Poker.Suits.CLUB));
    pokers.add(new Poker(10, Poker.Suits.CLUB));
    playerAction.setPokers(pokers);
    playerAction.buildPokerModel();

    // AI应该出最大的10
    System.out.println(playerAction.outPoker(null, null));
    ;
  }

  private static void test() {
    List<List<Poker>> allocPokers = PokerUtil.allocPokers();

    PlayerNode playerNode = new PlayerNode();
    playerNode.setPlayer("1");
    PlayerNode playerNode2 = new PlayerNode();
    playerNode2.setPlayer("2");

    List<Poker> pokers = new ArrayList<>();
    pokers.add(new Poker(3, Poker.Suits.SPADE));
    pokers.add(new Poker(3, Poker.Suits.CLUB));
    pokers.add(new Poker(3, Poker.Suits.DIAMOND));
    pokers.add(new Poker(4, Poker.Suits.SPADE));
    pokers.add(new Poker(4, Poker.Suits.DIAMOND));
    pokers.add(new Poker(4, Poker.Suits.CLUB));
    pokers.add(new Poker(5, Poker.Suits.SPADE));
    pokers.add(new Poker(5, Poker.Suits.DIAMOND));
    pokers.add(new Poker(5, Poker.Suits.CLUB));
    pokers.add(new Poker(6, Poker.Suits.HEART));
    pokers.add(new Poker(7, Poker.Suits.SPADE));
    pokers.add(new Poker(8, Poker.Suits.CLUB));
    pokers.add(new Poker(8, Poker.Suits.SPADE));
    pokers.add(new Poker(9, Poker.Suits.CLUB));
    pokers.add(new Poker(9, Poker.Suits.DIAMOND));
    pokers.add(new Poker(10, Poker.Suits.CLUB));
    pokers.add(new Poker(10, Poker.Suits.DIAMOND));

    AIPlayerAction playerAction = new AIPlayerAction(playerNode);
//        playerAction.setPokers(allocPokers.get(0));
//        playerAction.setLastPokers(allocPokers.get(3));
    playerAction.setPokers(pokers);
    playerAction.buildPokerModel();

    System.out.println("手牌：" + playerAction.getPokers());
    System.out.println("叫分" + playerAction.callScore(0));
    System.out.println("飞机：" + playerAction.getPlain());
    System.out.println("联对：" + playerAction.getShunzi(false));
    System.out.println("单顺子：" + playerAction.getShunzi(true));
    System.out.println("三张：" + playerAction.getThree());
    System.out.println("对牌：" + playerAction.getPair());
    System.out.println("单牌：" + playerAction.getSingle());

    System.out.println("出牌：" + playerAction.outPoker(null, null));
  }

}
